package cn.iocoder.yudao.module.system.service.gugu;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * getAllSuitableMajors 缓存优化示例
 * 
 * 这个文件展示了如何为 getAllSuitableMajors 方法添加缓存机制
 * 以提升查询性能和减少数据库压力
 */
@Slf4j
@Service
@CacheConfig(cacheNames = "suitableMajors")
public class UserProfileServiceCacheOptimized {

    /**
     * 缓存配置的 getAllSuitableMajors 方法
     * 
     * 缓存策略：
     * 1. 缓存键基于用户的关键查询参数
     * 2. 缓存时间30分钟，避免数据过期
     * 3. 条件缓存，只缓存成功的查询结果
     */
    @Cacheable(
        key = "#profile.getCacheKey()", 
        condition = "#profile != null && #profile.getTotalScore() > 0",
        unless = "#result == null || #result.isEmpty()"
    )
    public Map<String, Object> getAllSuitableMajors(UserProfileInfo profile) {
        log.info("执行数据库查询 - 缓存未命中，用户：{}", profile.getCacheKey());
        
        // 原有的查询逻辑
        return performActualQuery(profile);
    }

    /**
     * 分页查询的缓存版本
     */
    @Cacheable(
        key = "#profile.getCacheKey() + '_page_' + #pageNo + '_' + #pageSize + '_' + #majorType",
        condition = "#profile != null && #pageNo > 0 && #pageSize > 0"
    )
    public Map<String, Object> getAllSuitableMajorsWithPagination(
            UserProfileInfo profile, Integer pageNo, Integer pageSize, String majorType) {
        
        log.info("执行分页查询 - 缓存未命中，用户：{}，页码：{}，类型：{}", 
                profile.getCacheKey(), pageNo, majorType);
        
        // 原有的分页查询逻辑
        return performActualPaginationQuery(profile, pageNo, pageSize, majorType);
    }

    /**
     * 清除用户相关的所有缓存
     * 当用户更新分数、选科等信息时调用
     */
    @CacheEvict(
        key = "#profile.getCacheKey()",
        allEntries = false
    )
    public void clearUserCache(UserProfileInfo profile) {
        log.info("清除用户缓存：{}", profile.getCacheKey());
    }

    /**
     * 清除所有缓存
     * 当基础数据更新时调用（如专业录取数据、招生计划数据更新）
     */
    @CacheEvict(allEntries = true)
    public void clearAllCache() {
        log.info("清除所有适合专业缓存");
    }

    // 模拟原有查询方法
    private Map<String, Object> performActualQuery(UserProfileInfo profile) {
        // 这里是原有的 getAllSuitableMajors 实现
        return null;
    }

    private Map<String, Object> performActualPaginationQuery(
            UserProfileInfo profile, Integer pageNo, Integer pageSize, String majorType) {
        // 这里是原有的分页查询实现
        return null;
    }
}

/**
 * UserProfileInfo 缓存键生成优化
 */
class UserProfileInfoCacheOptimized {
    
    /**
     * 生成缓存键
     * 基于用户的关键查询参数生成唯一标识
     */
    public String getCacheKey() {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append("user_")
                  .append(getProvince()).append("_")
                  .append(getTotalScore()).append("_")
                  .append(getYear()).append("_");
        
        // 添加选科信息
        if (getSubjects() != null && !getSubjects().isEmpty()) {
            keyBuilder.append("subjects_")
                      .append(String.join(",", getSubjects())).append("_");
        }
        
        // 添加意向专业信息
        if (getInterestedMajorCategories() != null && !getInterestedMajorCategories().isEmpty()) {
            keyBuilder.append("interests_")
                      .append(String.join(",", getInterestedMajorCategories())).append("_");
        }
        
        // 添加自定义分数范围
        if (getMinScore() != null) {
            keyBuilder.append("min_").append(getMinScore()).append("_");
        }
        if (getMaxScore() != null) {
            keyBuilder.append("max_").append(getMaxScore()).append("_");
        }
        
        // 添加类型名称
        if (getTypeName() != null && !getTypeName().isEmpty()) {
            keyBuilder.append("type_").append(getTypeName());
        }
        
        return keyBuilder.toString();
    }
    
    // 模拟getter方法
    private String getProvince() { return ""; }
    private Integer getTotalScore() { return 0; }
    private Integer getYear() { return 0; }
    private java.util.List<String> getSubjects() { return null; }
    private java.util.List<String> getInterestedMajorCategories() { return null; }
    private Integer getMinScore() { return null; }
    private Integer getMaxScore() { return null; }
    private String getTypeName() { return ""; }
}

/**
 * Redis 缓存配置示例
 */
@org.springframework.context.annotation.Configuration
@org.springframework.cache.annotation.EnableCaching
class CacheConfiguration {

    /**
     * Redis 缓存管理器配置
     */
    @org.springframework.context.annotation.Bean
    public org.springframework.data.redis.cache.RedisCacheManager cacheManager(
            org.springframework.data.redis.connection.RedisConnectionFactory connectionFactory) {
        
        // 默认缓存配置
        org.springframework.data.redis.cache.RedisCacheConfiguration defaultConfig = 
            org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(java.time.Duration.ofMinutes(30)) // 默认30分钟过期
                .serializeKeysWith(org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair
                    .fromSerializer(new org.springframework.data.redis.serializer.StringRedisSerializer()))
                .serializeValuesWith(org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair
                    .fromSerializer(new org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer()));

        // 特定缓存配置
        java.util.Map<String, org.springframework.data.redis.cache.RedisCacheConfiguration> cacheConfigurations = 
            new java.util.HashMap<>();
        
        // 适合专业缓存：30分钟过期
        cacheConfigurations.put("suitableMajors", defaultConfig
            .entryTtl(java.time.Duration.ofMinutes(30)));
        
        // 分页缓存：15分钟过期（更短的过期时间）
        cacheConfigurations.put("suitableMajorsPage", defaultConfig
            .entryTtl(java.time.Duration.ofMinutes(15)));
        
        // 基础数据缓存：2小时过期
        cacheConfigurations.put("basicData", defaultConfig
            .entryTtl(java.time.Duration.ofHours(2)));

        return org.springframework.data.redis.cache.RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaultConfig)
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }

    /**
     * 缓存键生成器
     */
    @org.springframework.context.annotation.Bean
    public org.springframework.cache.interceptor.KeyGenerator customKeyGenerator() {
        return (target, method, params) -> {
            StringBuilder keyBuilder = new StringBuilder();
            keyBuilder.append(target.getClass().getSimpleName())
                      .append("_")
                      .append(method.getName());
            
            for (Object param : params) {
                if (param != null) {
                    keyBuilder.append("_").append(param.toString());
                }
            }
            
            return keyBuilder.toString();
        };
    }
}

/**
 * 缓存监控和统计
 */
@org.springframework.stereotype.Component
class CacheMonitor {
    
    private final org.springframework.data.redis.core.RedisTemplate<String, Object> redisTemplate;
    
    public CacheMonitor(org.springframework.data.redis.core.RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 获取缓存统计信息
     */
    public java.util.Map<String, Object> getCacheStats() {
        java.util.Map<String, Object> stats = new java.util.HashMap<>();
        
        // 获取所有缓存键
        java.util.Set<String> keys = redisTemplate.keys("suitableMajors*");
        stats.put("totalCacheKeys", keys != null ? keys.size() : 0);
        
        // 计算缓存大小
        long totalMemory = 0;
        if (keys != null) {
            for (String key : keys) {
                Long size = redisTemplate.opsForValue().size(key);
                if (size != null) {
                    totalMemory += size;
                }
            }
        }
        stats.put("totalMemoryUsage", totalMemory);
        
        return stats;
    }
    
    /**
     * 清理过期缓存
     */
    @org.springframework.scheduling.annotation.Scheduled(fixedRate = 3600000) // 每小时执行一次
    public void cleanExpiredCache() {
        log.info("开始清理过期缓存");
        
        java.util.Set<String> keys = redisTemplate.keys("suitableMajors*");
        if (keys != null) {
            int cleanedCount = 0;
            for (String key : keys) {
                Long ttl = redisTemplate.getExpire(key);
                if (ttl != null && ttl <= 0) {
                    redisTemplate.delete(key);
                    cleanedCount++;
                }
            }
            log.info("清理完成，删除了 {} 个过期缓存", cleanedCount);
        }
    }
}

/**
 * 缓存预热服务
 */
@org.springframework.stereotype.Service
class CacheWarmupService {
    
    private final UserProfileServiceCacheOptimized userProfileService;
    
    public CacheWarmupService(UserProfileServiceCacheOptimized userProfileService) {
        this.userProfileService = userProfileService;
    }
    
    /**
     * 系统启动时预热常用缓存
     */
    @org.springframework.context.event.EventListener
    public void warmupCache(org.springframework.context.event.ContextRefreshedEvent event) {
        log.info("开始缓存预热");
        
        // 预热常用省份和分数段的查询
        String[] provinces = {"北京", "上海", "广东", "江苏", "浙江"};
        int[] scores = {500, 550, 600, 650};
        
        for (String province : provinces) {
            for (int score : scores) {
                try {
                    UserProfileInfo profile = createSampleProfile(province, score);
                    userProfileService.getAllSuitableMajors(profile);
                    Thread.sleep(100); // 避免过快请求
                } catch (Exception e) {
                    log.warn("缓存预热失败：省份={}, 分数={}", province, score, e);
                }
            }
        }
        
        log.info("缓存预热完成");
    }
    
    private UserProfileInfo createSampleProfile(String province, int score) {
        // 创建示例用户配置
        return new UserProfileInfo(); // 实际实现中需要设置具体参数
    }
}
